'''
XML输出解析器示例

XML（eXtensible Markup Language）是一种可扩展的标记语言，类似于HTML但更灵活。
在大模型应用中，XML格式常用于：
1. 结构化数据提取
2. 复杂嵌套数据表示
3. 与现有XML系统集成
4. 清晰的数据层次结构

安装安全XML处理库：
pip install defusedxml
'''

import os
from dotenv import load_dotenv
from langchain_core.output_parsers import XMLOutputParser
from langchain_core.prompts import PromptTemplate
from langchain_openai import ChatOpenAI

# 自动加载 .env 文件中的环境变量
load_dotenv()

# 定义大模型
llm = ChatOpenAI(
    api_key=os.getenv("MODELSCOPE_API_KEY"),
    openai_api_base=os.getenv("MODELSCOPE_API_BASE"),
    model="deepseek-ai/DeepSeek-R1-0528",
    streaming=True,
    temperature=0.7
)

# 用户查询 - 请求生成结构化数据
query = "生成李兰迪的所有作品，按最新时间排序。"

# ========== XML输出解析器配置 ==========
'''
XMLOutputParser 的两种使用方式：

1. 默认方式：让大模型自由决定XML标签
   parser = XMLOutputParser()

2. 自定义标签：指定允许使用的XML标签，控制输出结构
   parser = XMLOutputParser(tags=["movies", "actor", "film", "name", "genre", "description"])

自定义标签的优势：
- 控制数据结构一致性
- 避免模型使用不相关的标签
- 便于后续XML解析和处理
'''

# 设置解析器 - 使用自定义标签控制输出结构
parser = XMLOutputParser(tags=["movies", "actor", "film", "name", "genre", "description", "year"])

# ========== 提示模板配置 ==========
prompt = PromptTemplate(
    template="""回答用户的查询。
请使用以下XML标签来组织回答：{format_instructions}
用户查询：{query}
请确保使用正确的XML格式，包括开始标签和结束标签。""",
    input_variables=["query"],
    # parser.get_format_instructions() 自动生成XML格式指令告诉大模型如何构建响应
    partial_variables={"format_instructions": parser.get_format_instructions()}
)

# 可以打印出来查看XML解析器自动生成的格式指令
print("=== XML解析器自动生成的格式指令 ===")
print(parser.get_format_instructions())
print("=" * 60)

'''
parser.get_format_instructions() 会生成类似这样的指令：
"The output should be formatted as a XML file.
You should only use the following tags: movies, actor, film, name, genre, description, year"

这样大模型就知道：
- 必须返回XML格式
- 只能使用指定的标签
- 需要遵循XML语法规则（标签闭合、嵌套等）
'''

# ========== 处理链执行 ==========
# 创建处理链：提示模板 -> 大模型
chain = prompt | llm

print("=== 大模型原始XML响应 ===")
response = chain.invoke({"query": query})
print(response.content)
print("=" * 60)

print("=== 解析后的XML结构 ===")
try:
    # 使用XML解析器解析大模型的响应
    xml_output = parser.parse(response.content)
    print("解析成功:", xml_output)
except Exception as e:
    print("解析失败:", e)
    print("这可能是因为大模型没有完全遵循XML格式")

# ========== XML vs JSON 对比 ==========
print("\n" + "="*80)
print("XML格式的优势：")
print("1. 层次结构清晰：适合表示复杂的嵌套数据")
print("2. 自描述性强：标签名本身就说明了数据含义")
print("3. 扩展性好：可以轻松添加新的标签和属性")
print("4. 广泛支持：几乎所有编程语言都有XML处理库")

print("\n预期XML结构示例：")
expected_xml = """
<movies>
    <actor>
        <name>李兰迪</name>
    </actor>
    <film>
        <name>作品名称1</name>
        <year>2023</year>
        <genre>剧情</genre>
        <description>作品描述...</description>
    </film>
    <film>
        <name>作品名称2</name>
        <year>2022</year>
        <genre>爱情</genre>
        <description>作品描述...</description>
    </film>
</movies>
"""
print(expected_xml)

# ========== 流式输出演示 ==========
print("\n=== XML流式输出演示 ===")
print("注意：XML流式输出时，大模型会逐步生成完整的XML结构")
for i, chunk in enumerate(chain.stream({"query": query})):
    print(f"片段 {i+1}: {chunk.content}")

# ========== 安全考虑 ==========
print("\n" + "="*80)
print("安全提示：")
print("1. 使用defusedxml库可以防止XML炸弹攻击")
print("2. 限制自定义标签可以避免模型生成恶意结构")
print("3. 始终验证和清理XML输入")